import { useEffect, useRef, useState } from 'react'
import pianoPlayerEvent, { type MusicKey } from '@/event/pianoPlayerEvent'
import { getMusicIndexByNote, getMusicKeyIndex } from '@/lib/music/data'
import NumberAnimationValue from '@/lib/music/NumberAnimationValue'
import { debounce } from '@/utils/common'
import { MusicPlayerSong } from '@/types/music'
import styles from './index.module.scss'
const canvasWidth = 2000
const canvasHeight = 500
interface Props {
  // /**
  //  * 开始时间
  //  */
  // startTime?: number
  // /**
  //  * 结束时间
  //  */
  // endTime?: number
  // song?: MusicPlayerSong
}
const defaultTimeOffset = 30 * 1000

function formatTime(time: number) {
  let second = Math.floor(time / 1000)
  const minute = Math.floor(second / 60)
  second = second % 60

  return `${minute}:${second}${second < 10 ? '0' : ''}`
}
/**
 * 音符可视化
 * @returns
 */
export default function NoteVisualzation1(_props: Props) {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  // const [currentTimeState, setCurrentTimeState] = useState(0)
  const currentTime = useRef(null as null | NumberAnimationValue)
  const notes = useRef([] as { key: MusicKey; dateTime: number }[])
  const [songState, setSongState] = useState<MusicPlayerSong | null>(null)
  const song = useRef(null as MusicPlayerSong | null)
  const [isDrag, setIsDrag] = useState(false)
  const [mousePosState, setMousePos] = useState({ x: 0, y: 0 } as {
    x: number
    y: number
  } | null)
  const mousePos = useRef({ x: 0, y: 0 } as { x: number; y: number } | null)
  useEffect(() => {
    mousePos.current = mousePosState
  }, [mousePosState])
  const startTimeRef = useRef(0)
  const endTimeRef = useRef(0)
  const canvasScale = useRef(1)
  useEffect(() => {
    song.current = songState
    if (song.current) {
      const u1 = pianoPlayerEvent.on('playSongTimeUpdate', (t) => {
        if (currentTime.current) {
          currentTime.current.value = t
          // setCurrentTimeState(t)
        }
      })
      return () => {
        u1()
      }
    } else {
      const uninstall = pianoPlayerEvent.on('noteDown', (key) => {
        notes.current.push({ dateTime: Date.now(), key })
      })
      return () => uninstall()
    }
  }, [songState])
  useEffect(() => {
    currentTime.current = new NumberAnimationValue(0, 0.1)
    const songPackage = {
      value: null as MusicPlayerSong | null,
      currentTime: 0,
    }
    pianoPlayerEvent.emit('loadSong', songPackage)
    currentTime.current.value = songPackage.currentTime
    setSongState(songPackage.value)
    const u1 = pianoPlayerEvent.on('onPlayerSongStop', () => {
      currentTime.current!.value = 0
      setSongState(null)
    })
    const u2 = pianoPlayerEvent.on('playerSong', (song) => {
      currentTime.current!.value = 0
      setSongState(song)
    })
    return () => {
      u1()
      u2()
    }
  }, [])
  useEffect(() => {
    const onReset = debounce(() => {
      if (canvasRef.current)
        canvasScale.current =
          canvasRef.current.getBoundingClientRect().width / canvasWidth
    }, 500)
    onReset()
    window.addEventListener('resize', onReset)
    return () => {
      window.removeEventListener('resize', onReset)
    }
  }, [])
  useEffect(() => {
    if (isDrag) {
      const onMouseUp = () => {
        setIsDrag((drag) => {
          if (drag && song.current) {
            pianoPlayerEvent.emit(
              'songReplay',
              song.current,
              currentTime.current!.realValue
            )
          }
          return false
        })
      }
      const onMouseMove = (e: MouseEvent) => {
        if (!mousePos.current) return
        if (!song.current) return
        mousePos.current.x += e.movementX / canvasScale.current
        currentTime.current!.value =
          (mousePos.current.x / canvasWidth) * song.current.playerTime
      }
      window.addEventListener('mouseup', onMouseUp)
      window.addEventListener('mousemove', onMouseMove)
      return () => {
        window.removeEventListener('mouseup', onMouseUp)
        window.removeEventListener('mousemove', onMouseMove)
      }
    }
  }, [isDrag])

  useEffect(() => {
    let isEnd = false
    const ctx = canvasRef.current?.getContext('2d')
    const update = () => {
      if (isEnd) return
      if (!ctx) return
      const startTime = startTimeRef.current || Date.now() - defaultTimeOffset
      const endTime = endTimeRef.current || Date.now()
      ctx.fillStyle = '#2f343a'
      ctx.fillRect(0, 0, canvasWidth, canvasHeight)
      // 绘制时间刻度
      ctx.fillStyle = '#5f5f5f'
      let w = 4
      let h = 12
      if (!song.current) {
        w = 5
        h = 20
        const timeInterval = endTime - startTime
        for (let i = 0; i < timeInterval * 2; i += 1000) {
          const offset = Date.now() % timeInterval
          const height = i % 10000 == 0 ? 20 : 8
          const width = i % 10000 == 0 ? 3 : 2
          const x = ((i - offset) / timeInterval) * canvasWidth
          ctx.fillRect(x, canvasHeight - height, width, height)
        }
      } else {
        const timeInterval = song.current.playerTime
        const scaleDifference = (10000 / timeInterval) * canvasWidth
        for (let i = 0; i < timeInterval; i += 1000) {
          const height = i % 10000 == 0 ? 20 : 8
          const width = i % 10000 == 0 ? 3 : 2
          const x = (i / timeInterval) * canvasWidth
          ctx.fillRect(x, canvasHeight - height, width, height)
        }
        // 绘制时间文字
        ctx.fillStyle = '#929292'
        ctx.font = '20px Arial bold'
        ctx.textAlign = 'left'
        for (let i = 0; i < timeInterval; i += 10000) {
          const x = (i / timeInterval) * canvasWidth
          ctx.fillText(
            formatTime(i),
            x + scaleDifference / 20,
            canvasHeight - 10
          )
        }
      }

      const drawHeight = canvasHeight - 30
      const itemHeight = drawHeight / 36

      if (song.current) {
        const notes = song.current.data
        for (let item of notes) {
          if (item.time > song.current.playerTime) continue
          const x = (item.time / song.current.playerTime) * canvasWidth
          const index = getMusicIndexByNote(item.note)
          const y = itemHeight * index.index + itemHeight / 1.5
          ctx.fillStyle = index.type == 'white' ? '#23aaf2' : 'black'
          ctx.fillRect(x - w / 2, y - h / 2, w, h)
        }
        // 绘制时间线
        ctx.fillStyle = '#929292'
        const x =
          (currentTime.current!.update() / song.current.playerTime) *
          canvasWidth
        ctx.fillRect(x, 0, 2, canvasHeight)
      } else {
        for (let item of notes.current) {
          if (item.dateTime < startTime) continue
          if (item.dateTime > endTime) continue
          const x =
            ((item.dateTime - startTime) / (endTime - startTime)) * canvasWidth
          const index = getMusicKeyIndex(item.key)
          const y = itemHeight * index.index + itemHeight / 1.5
          ctx.fillStyle = index.type == 'white' ? '#23aaf2' : 'black'
          ctx.fillRect(x - w / 2, y - h / 2, w, h)
        }
      }

      requestAnimationFrame(update)
    }
    update()
    return () => {
      isEnd = true
    }
  }, [])
  const isSureDragState = () => {
    if (!song.current) return false
    if (isDrag) return true
    if (!mousePosState) return false
    if (!currentTime.current) return false

    const x =
      (currentTime.current?.value / song.current.playerTime) * canvasWidth
    return Math.abs(x - mousePosState.x) < 20
  }

  return (
    <div style={{ margin: '10px auto', display: 'flex' }}>
      <canvas
        onMouseMove={(e) => {
          if (isDrag) {
            return
          }
          setMousePos({
            x: e.nativeEvent.layerX / canvasScale.current,
            y: e.nativeEvent.layerY / canvasScale.current,
          })
        }}
        onMouseOut={() => {
          if (isDrag) return
          setMousePos(null)
        }}
        onMouseDown={(_) => {
          if (!song.current) return
          if (isSureDragState()) {
            setIsDrag(true)
            pianoPlayerEvent.emit('songPause', song.current)
          }
        }}
        ref={canvasRef}
        width={canvasWidth}
        height={canvasHeight}
        className={styles['canvas']}
        style={{
          cursor: isSureDragState() ? 'ew-resize' : 'default',
        }}></canvas>
    </div>
  )
}
